package utils

import (
	"gozero-microservices/gateway-api/define"
	"log"
	"reflect"

	sentinel "github.com/alibaba/sentinel-golang/api"
	"github.com/alibaba/sentinel-golang/core/circuitbreaker"
	"github.com/alibaba/sentinel-golang/core/flow"
	"github.com/alibaba/sentinel-golang/core/isolation"
)

var LimiterResource string
var CircuitbreakerResource string
var IsolationResource string

func InitSentinel() {
	//初始化 sentinel
	err := sentinel.InitWithConfigFile("etc/sentinel.yaml")
	if err != nil {
		log.Fatal("err", err)
		return
	}

	LimiterResource = define.LimiterResource
	limiterData := define.LimiterData[LimiterResource].(map[string]interface{})

	// 当前流量控制器的Token计算策略。
	var tokenCalculateStrategy flow.TokenCalculateStrategy
	tcs := limiterData["tokenCalculateStrategy"].(int)
	if tcs == 0 {
		tokenCalculateStrategy = flow.Direct
	} else if tcs == 1 {
		tokenCalculateStrategy = flow.WarmUp
	}

	// 表示流量控制器的控制策略
	var controlBehavior flow.ControlBehavior
	cb := limiterData["controlBehavior"].(int)
	if cb == 0 {
		controlBehavior = flow.Reject
	} else if cb == 1 {
		controlBehavior = flow.Throttling
	}

	////////////////////////////////////////////// go语言实现暂未发现appLimit即限制客户端每秒请求次数 //////////////////////////////////////////////
	if _, err := flow.LoadRules([]*flow.Rule{
		// Resource：资源名，即规则的作用目标。
		// TokenCalculateStrategy: 当前流量控制器的Token计算策略。Direct表示直接使用字段 Threshold 作为阈值；WarmUp表示使用预热方式计算Token的阈值。
		// ControlBehavior: 表示流量控制器的控制策略；Reject表示超过阈值直接拒绝，Throttling表示匀速排队。
		// Threshold: 表示流控阈值；如果字段 StatIntervalInMs 是1000(也就是1秒)，那么Threshold就表示QPS，流量控制器也就会依据资源的QPS来做流控。
		// RelationStrategy: 调用关系限流策略，CurrentResource表示使用当前规则的resource做流控；AssociatedResource表示使用关联的resource做流控，关联的resource在字段 RefResource 定义；
		// RefResource: 关联的resource；
		// WarmUpPeriodSec: 预热的时间长度，该字段仅仅对 WarmUp 的TokenCalculateStrategy生效；
		// WarmUpColdFactor: 预热的因子，默认是3，该值的设置会影响预热的速度，该字段仅仅对 WarmUp 的TokenCalculateStrategy生效；
		// MaxQueueingTimeMs: 匀速排队的最大等待时间，该字段仅仅对 Throttling ControlBehavior生效；
		// StatIntervalInMs: 规则对应的流量控制器的独立统计结构的统计周期。如果StatIntervalInMs是1000，也就是统计QPS。
		{
			Resource:               LimiterResource,                               //资源名
			Threshold:              float64(limiterData["threshold"].(int)),       //表示流控阈值；如果字段 StatIntervalInMs 是1000(也就是1秒)，那么Threshold就表示QPS，流量控制器也就会依据资源的QPS来做流控
			StatIntervalInMs:       uint32(limiterData["statIntervalInMs"].(int)), //规则对应的流量控制器的独立统计结构的统计周期。如果StatIntervalInMs是1000，也就是统计QPS
			TokenCalculateStrategy: tokenCalculateStrategy,                        // flow.Direct
			ControlBehavior:        controlBehavior,                               //flow.Reject
		},
	}); err != nil {
		log.Fatalf("Unexpected error: %+v", err)
		return
	}

	////////////////////////////////////////////////////// 熔断 //////////////////////////////////////////////////////
	CircuitbreakerResource = define.CircuitbreakerResource
	var circuitbreakerStrategy circuitbreaker.Strategy
	if CircuitbreakerResource == "request-slow-ratio" {
		circuitbreakerStrategy = circuitbreaker.SlowRequestRatio
	} else if CircuitbreakerResource == "request-error-ratio" {
		circuitbreakerStrategy = circuitbreaker.ErrorRatio
	} else {
		circuitbreakerStrategy = circuitbreaker.ErrorCount
	}

	circuitbreakerData := define.CircuitbreakerData[CircuitbreakerResource].(map[string]interface{})

	var threshold float64
	t := reflect.TypeOf(circuitbreakerData["threshold"]).Kind()
	if t == reflect.Int {
		threshold = float64(circuitbreakerData["threshold"].(int))
	} else {
		threshold = circuitbreakerData["threshold"].(float64)
	}

	if _, err := circuitbreaker.LoadRules([]*circuitbreaker.Rule{
		{
			// 熔断器规则生效的埋点资源的名称；
			Resource: CircuitbreakerResource,
			// 选择以慢调用比例 (SlowRequestRatio) 作为阈值，需要设置允许的最大响应时间（MaxAllowedRtMs），请求的响应时间大于该值则统计为慢调用。
			// 通过 Threshold 字段设置触发熔断的慢调用比例，取值范围为 [0.0, 1.0]。
			// 在单位统计时长内请求数目大于设置的最小请求数目，并且慢调用的比例大于阈值，则接下来的熔断时长内请求会自动被熔断。
			// 经过熔断时长后熔断器会进入探测恢复状态，若接下来的一个请求响应时间小于设置的最大 RT 则结束熔断，
			// 若大于设置的最大 RT 则会再次被熔断。
			Strategy: circuitbreakerStrategy,
			// 即熔断触发后持续的时间（单位为 ms）。
			// 资源进入熔断状态后，在配置的熔断时长内，请求都会快速失败。
			// 熔断结束后进入探测恢复模式（HALF-OPEN）。
			RetryTimeoutMs: uint32(circuitbreakerData["retryTimeoutMs"].(int)),
			// 静默数量，触发熔断的最小请求数目，若当前统计周期内的请求数小于此值，即使达到熔断条件规则也不会触发。
			MinRequestAmount: uint64(circuitbreakerData["minRequestAmount"].(int)),
			// 统计的时间窗口长度（单位为 ms）。
			StatIntervalMs: uint32(circuitbreakerData["statIntervalMs"].(int)),
			// 仅对慢调用熔断策略生效，MaxAllowedRtMs 是判断请求是否是慢调用的临界值，
			// 也就是如果请求的response time小于或等于MaxAllowedRtMs，那么就不是慢调用；
			// 如果response time大于MaxAllowedRtMs，那么当前请求就属于慢调用。
			MaxAllowedRtMs: uint64(circuitbreakerData["maxAllowedRtMs"].(int)),
			// 对于慢调用熔断策略, Threshold表示是慢调用比例的阈值(小数表示，比如0.1表示10%)，
			// 也就是如果当前资源的慢调用比例如果高于Threshold，那么熔断器就会断开；否则保持闭合状态。
			// 对于错误比例策略，Threshold表示的是错误比例的阈值(小数表示，比如0.1表示10%)。
			// 对于错误数策略，Threshold是错误计数的阈值。
			Threshold: threshold,
		},
	}); err != nil {
		log.Fatalf("Unexpected error: %+v", err)
		return
	}

	/////////////////////////////////////////// 并发隔离控制 /////////////////////////////////////////////////
	_, err = isolation.LoadRules([]*isolation.Rule{
		{
			Resource:   define.IsolationResource,
			MetricType: isolation.Concurrency,
			Threshold:  uint32(define.IsolationThreshold),
		},
	})
	if err != nil {
		log.Fatalf("Unexpected error: %+v", err)
		return
	}
}
